//************************************ bs::framework - Copyright 2018 Marko Pintera **************************************//
//*********** Licensed under the MIT license. See LICENSE.md for full terms. This notice is not to be removed. ***********//
#pragma once

#include "BsRenderBeastPrerequisites.h"
#include "Renderer/BsRendererMaterial.h"
#include "Renderer/BsParamBlocks.h"

namespace bs { namespace ct
{
	class VisibleReflProbeData;
	class VisibleLightData;

	/** @addtogroup RenderBeast
	 *  @{
	 */

	BS_PARAM_BLOCK_BEGIN(LightGridParamDef)
		BS_PARAM_BLOCK_ENTRY(Vector4I, gLightCounts)
		BS_PARAM_BLOCK_ENTRY(Vector2I, gLightStrides)
		BS_PARAM_BLOCK_ENTRY(INT32, gNumReflProbes)
		BS_PARAM_BLOCK_ENTRY(INT32, gNumCells)
		BS_PARAM_BLOCK_ENTRY(Vector3I, gGridSize)
		BS_PARAM_BLOCK_ENTRY(INT32, gMaxNumLightsPerCell)
		BS_PARAM_BLOCK_ENTRY(Vector2I, gGridPixelSize)
	BS_PARAM_BLOCK_END

	extern LightGridParamDef gLightGridParamDefDef;

	/** 
	 * Shader that creates a linked list for each light grid cell, containing which lights and reflection probes affects
	 * each cell. 
	 */
	class LightGridLLCreationMat : public RendererMaterial<LightGridLLCreationMat>
	{
		RMAT_DEF_CUSTOMIZED("LightGridLLCreation.bsl");

	public:
		LightGridLLCreationMat();

		/** Binds parameter buffers and prepares any internal buffers. Must be called before execute(). */
		void setParams(const Vector3I& gridSize, const SPtr<GpuParamBlockBuffer>& gridParams, 
					   const SPtr<GpuBuffer>& lightsBuffer, const SPtr<GpuBuffer>& probesBuffer);

		/** Binds the material for rendering, sets up per-camera parameters and executes it. */
		void execute(const RendererView& view);

		/** Returns the buffers generated by execute(). */
		void getOutputs(SPtr<GpuBuffer>& lightsLLHeads, SPtr<GpuBuffer>& lightsLL, SPtr<GpuBuffer>& probesLLHeads, 
			SPtr<GpuBuffer>& probesLL) const;
	private:
		GpuParamBuffer mLightBufferParam;
		GpuParamBuffer mLightsCounterParam;
		GpuParamBuffer mLightsLLHeadsParam;
		GpuParamBuffer mLightsLLParam;

		GpuParamBuffer mProbesBufferParam;
		GpuParamBuffer mProbesCounterParam;
		GpuParamBuffer mProbesLLHeadsParam;
		GpuParamBuffer mProbesLLParam;

		SPtr<GpuBuffer> mLightsCounter;
		SPtr<GpuBuffer> mLightsLLHeads;
		SPtr<GpuBuffer> mLightsLL;

		SPtr<GpuBuffer> mProbesCounter;
		SPtr<GpuBuffer> mProbesLLHeads;
		SPtr<GpuBuffer> mProbesLL;

		UINT32 mBufferNumCells;
		Vector3I mGridSize;
	};

	/** Shader that reduces the linked list created by LightGridLLCreationMat into a sequential array. */
	class LightGridLLReductionMat : public RendererMaterial<LightGridLLReductionMat>
	{
		RMAT_DEF_CUSTOMIZED("LightGridLLReduction.bsl");

	public:
		LightGridLLReductionMat();

		/** Binds parameter buffers and prepares any internal buffers. Must be called before execute(). */
		void setParams(const Vector3I& gridSize, const SPtr<GpuParamBlockBuffer>& gridParams, 
			const SPtr<GpuBuffer>& lightLLHeads, const SPtr<GpuBuffer>& lightLL,
			const SPtr<GpuBuffer>& probeLLHeads, const SPtr<GpuBuffer>& probeLL);

		/** Binds the material for renderingand executes it. */
		void execute(const RendererView& view);

		/** Returns the buffers generated by execute(). */
		void getOutputs(SPtr<GpuBuffer>& gridLightOffsetsAndSize, SPtr<GpuBuffer>& gridLightIndices,
			SPtr<GpuBuffer>& gridProbeOffsetsAndSize, SPtr<GpuBuffer>& gridProbeIndices) const;
	private:
		GpuParamBuffer mLightsLLHeadsParam;
		GpuParamBuffer mLightsLLParam;

		GpuParamBuffer mProbesLLHeadsParam;
		GpuParamBuffer mProbesLLParam;

		GpuParamBuffer mGridDataCounterParam;

		GpuParamBuffer mGridLightOffsetAndSizeParam;
		GpuParamBuffer mGridLightIndicesParam;

		GpuParamBuffer mGridProbeOffsetAndSizeParam;
		GpuParamBuffer mGridProbeIndicesParam;

		SPtr<GpuBuffer> mGridDataCounter;

		SPtr<GpuBuffer> mGridLightOffsetAndSize;
		SPtr<GpuBuffer> mGridLightIndices;

		SPtr<GpuBuffer> mGridProbeOffsetAndSize;
		SPtr<GpuBuffer> mGridProbeIndices;

		UINT32 mBufferNumCells;
		Vector3I mGridSize;
	};

	/**	
	 * Helper class that is used for generating a grid in view space, whose cells contain information about lights 
	 * affecting them. Used for forward rendering. 
	 */
	class LightGrid
	{
	public:
		LightGrid();

		/** Updates the light grid from the provided view. */
		void updateGrid(const RendererView& view, const VisibleLightData& lightData, const VisibleReflProbeData& probeData, 
			bool noLighting);

		/** 
		 * Returns the buffers containing light indices per grid cell and global grid parameters. 
		 *
		 * @param[out]	gridLightOffsetsAndSize	Flattened array of grid cells, where each entry contains the number of 
		 *										lights affecting that cell, and a index into the @p gridLightIndices buffer.
		 * @param[out]	gridLightIndices		A list of light indices. Each cell's indices start at specific position and
		 *										are placed sequentially one after another. Lookup into this array is done
		 *										through offset & size provided by @p gridLightOffsetsAndSize. 
		 * @param[out]	gridProbeOffsetsAndSize	Flattened array of grid cells, where each entry contains the number of
		 *										reflection probes affecting that cell, and a index into the 
		 *										@p gridProbeIndices buffer.
		 * @param[out]	gridProbeIndices		A list of reflection probe indices. Each cell's indices start at specific
		 *										position and are placed sequentially one after another. Lookup into this
		 *										array is done through offset & size provided by @p gridProbeOffsetsAndSize.
		 * @param[out]	gridParams				Global grid parameter block.
		 */
		void getOutputs(SPtr<GpuBuffer>& gridLightOffsetsAndSize, SPtr<GpuBuffer>& gridLightIndices, 
			SPtr<GpuBuffer>& gridProbeOffsetsAndSize, SPtr<GpuBuffer>& gridProbeIndices, 
			SPtr<GpuParamBlockBuffer>& gridParams) const;

	private:
		SPtr<GpuParamBlockBuffer> mGridParamBuffer;
	};

	/** @} */
}}
